﻿using System;
using System.Drawing;
using WiMo.Games.Drawables;
using System.Collections.Generic;
using WiMo.Games.GapiDrawDisplay.Drawables;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
using GapiDrawNet;

namespace WiMo.Games.GapiDrawDisplay
{
    public class DisplayManager : IDisplay
    {
        #region IDisplay Members

        IntPtr _hwnd;
        List<IDrawable> _items;
        List<IDrawable> _temporaryObjects;
        
        Size _targetSize;
        Size _size;
        Vector2 _scale;
        byte _aspectRatio;
        int _dpi;
        private bool _isSceneDirty = true;
        public bool Diagnostics { get; set; }
        public DisplayStrategies DisplayStrategy { get; set; }
        GapiDisplay _display;

        public DisplayManager(IntPtr hwnd, Size bounds, int dpi)
        {
            _display = new GapiDisplay();

            _display.OpenDisplay(OpenDisplayOptions.FullScreen, hwnd, bounds.Width, bounds.Height);

            _items = new List<IDrawable>();
            _size = bounds;
            _scale = Vector2.One;
            _hwnd = hwnd;
            _dpi = dpi;
            DisplayStrategy = DisplayStrategies.Automatic;
        }

        public DisplayManager(IntPtr hwnd, Size targetSize, Size bounds, int dpi, byte aspectRatio)
            : this(hwnd, bounds, dpi)
        {
            _targetSize = targetSize;
            _size = bounds;
            _scale = new Vector2(Convert.ToSingle(_size.Width) / Convert.ToSingle(_targetSize.Width), Convert.ToSingle(_size.Height) / Convert.ToSingle(_targetSize.Height));
            _aspectRatio = aspectRatio;
        }

        public Location DisplayOffset
        {
            get { throw new NotImplementedException(); }
        }

        public byte AspectRatio
        {
            get { return _aspectRatio; }
            set { _aspectRatio = value; }
        }

        public int DPI
        {
            get { return _dpi; }
            set { _dpi = value; }
        }

        public string Name
        {
            get { return "GapiDraw"; }
        }

        public ITexture BackgroundImage { get; set; }
        public Color BackgroundColor { get; set; }

        public void Reset()
        {
            if (_temporaryObjects != null)
            {
                foreach (var tempObj in _temporaryObjects)
                    tempObj.Dispose();
            }

            _items = new List<IDrawable>();
            _temporaryObjects = new List<IDrawable>();

            SolidBrush backgroundBrush = new SolidBrush(BackgroundColor.ToDrawingColor());
            _display.FillRect(new System.Drawing.Rectangle(0, 0, _size.Width, _size.Height), BackgroundColor.ToDrawingColor());
            backgroundBrush.Dispose();

        }

        public void Add(IDrawable drawable)
        {
            _items.Add(drawable);
        }

        public ISpriteLine DrawLine(Location start, Location end, Color color, int width)
        {
            var tempLine = new Drawables.Line();
            tempLine.Scale = ScaleFactor;
            tempLine.Start = start;
            tempLine.End = end;
            tempLine.LineColor = color;
            tempLine.Width = width;
            tempLine.IsTransient = true;

            _temporaryObjects.Add(tempLine);
            _temporaryObjects.Add(tempLine);

            return tempLine;
        }

        public ISpriteText DrawText(PointF position, string message, ISpriteFont font, Color foreColor)
        {
            return DrawText(position, message, font, foreColor, Origin.TopLeft);
        }

        public ISpriteText DrawText(PointF position, string message, ISpriteFont font, Color foreColor, Origin origin)
        {
            var text = new Drawables.Text();
            text.Origin = origin;
            text.Scale = ScaleFactor;
            text.Position = position.Clone();
            text.ForeColor = foreColor;
            text.Font = font;
            text.Text = message;
            text.IsTransient = true;

            _temporaryObjects.Add(text);
            _items.Add(text);

            return text;
        }

        public ISpriteShape DrawRectangle(Rectangle rect, Color color, int width)
        {
            return DrawRectangle(rect, color, width, Origin.Center);
        }

        public ISpriteShape DrawRectangle(Rectangle rect, Color color, int width, Origin origin)
        {
            var tempRect = new Drawables.Rectangle();
            tempRect.Origin = origin;
            tempRect.Scale = ScaleFactor;
            tempRect.Rect = rect;
            tempRect.BorderWidth = width;
            tempRect.FillColor = Color.Transparent;
            tempRect.BorderColor = color;
            tempRect.IsTransient = true;

            _temporaryObjects.Add(tempRect);
            _items.Add(tempRect);

            return tempRect;
        }

        public ISpriteShape DrawCircle(PointF position, int radius, Color borderColor, int width)
        {
            var circle = new Drawables.Ellipse();
            circle.Scale = ScaleFactor;
            circle.Position = position.Clone();
            circle.Radius = radius;
            circle.BorderWidth = width;
            circle.BorderColor = borderColor;
            circle.IsTransient = true;

            _temporaryObjects.Add(circle);
            _items.Add(circle);

            return circle;
        }

        public ISpriteShape FillRectangle(Rectangle rect, Color fillColor, Color borderColor, int width)
        {
            return FillRectangle(rect, fillColor, borderColor, width, Origin.Center);
        }

        public ISpriteShape FillRectangle(Rectangle rect, Color fillColor, Color borderColor, int width, Origin origin)
        {
            var tempRect = new Drawables.Rectangle();
            tempRect.Origin = origin;
            tempRect.Scale = ScaleFactor;
            tempRect.Rect = rect;
            tempRect.BorderColor = borderColor;
            tempRect.FillColor = fillColor;
            tempRect.BorderWidth = width;
            tempRect.IsTransient = true;

            _temporaryObjects.Add(tempRect);
            _items.Add(tempRect);

            return tempRect;
        }

        public ISpriteShape FillCircle(PointF position, int radius, Color fillColor, Color borderColor, int width)
        {
            var circle = new Drawables.Ellipse();
            circle.Scale = ScaleFactor;
            circle.Position = position.Clone();
            circle.Radius = radius;
            circle.BorderWidth = width;
            circle.FillColor = fillColor;
            circle.BorderColor = borderColor;
            circle.IsTransient = true;

            _temporaryObjects.Add(circle);
            _items.Add(circle);

            return circle;
        }

        private Point GetOrigin(Origin origin, PointF original, Size size)
        {
            var point = new Point();

            if (origin == Origin.TopLeft)
                point = new Point(Convert.ToInt32(original.X), Convert.ToInt32(original.Y));
            else
                point = new Point(Convert.ToInt32(original.X - size.Width / 2), Convert.ToInt32(original.Y - size.Height / 2));

            return point;

            //            return new Point(Convert.ToInt32(point.X * ScaleFactor.X), Convert.ToInt32(point.Y * ScaleFactor.Y));
        }

        private Size GetSize(Size size)
        {
            return size;
            //return new Size(size.Width * ScaleFactor.X, size.Height * ScaleFactor.Y);
        }

        public void Render(ISpriteImage sprite)
        {
            var point = GetOrigin(sprite.Origin, sprite.Position, sprite.Size);
            var size = GetSize(sprite.Size);

            var srcRect = new System.Drawing.Rectangle(0, 0, sprite.Size.Width, sprite.Size.Height);
            var destRect = new System.Drawing.Rectangle(point.X, point.Y, sprite.Size.Width, sprite.Size.Height);

            var gapiSurface = sprite.DrawInformation as GapiSurface;

            if(gapiSurface == null)
            {
                var bitmap = sprite.TextureList[sprite.ImageIndex].TextureInformation as Bitmap;
                //var data = bitmap.LockBits(area.ToDrawingRect(), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                gapiSurface = new GapiSurface(sprite.Size.Width, sprite.Size.Height);
                sprite.DrawInformation = gapiSurface;
            }
           
            //if (sprite.ColorKey != Color.Transparent)
            //{
            //    var attr = new ImageAttributes();
            //    attr.SetColorKey(sprite.ColorKey.ToDrawingColor(), sprite.ColorKey.ToDrawingColor());
            //    _graphics.DrawImage(sprite.TextureList[sprite.ImageIndex].TextureInformation as Bitmap, destRect, 0, 0, size.Width, size.Height, GraphicsUnit.Pixel, attr);
            //}
            //else
            //    _graphics.DrawImage(sprite.TextureList[sprite.ImageIndex].TextureInformation as Bitmap, destRect, srcRect, GraphicsUnit.Pixel);


            var ticks = System.Environment.TickCount;
            _display.BltFast(sprite.Position.Point.X, sprite.Position.Point.Y, gapiSurface);
            System.Diagnostics.Debug.WriteLine("Time to render w/o color key " + (System.Environment.TickCount - ticks));
            //                _graphics.DrawImage(sprite.TextureList[sprite.ImageIndex].TextureInformation as Bitmap, point.X, point.Y);// destRect, srcRect, GraphicsUnit.Pixel);

        }

        public void Render(ISpriteText text)
        {
            //(text as Drawables.Text).Render(_graphics);

            return;
        }

        private Point[] GetPointArray(List<Location> locations, Point offset)
        {
            Point[] points = new Point[locations.Count];
            for (int i = 0; i < locations.Count; i++)
                points[i] = new Point(locations[i].X + offset.X, locations[i].Y + offset.Y);

            return points;
        }

        public void Render(ISpriteShape shape)
        {
            return;

            //var borderPen = new Pen(shape.BorderColor.ToDrawingColor(), Convert.ToSingle(shape.BorderWidth));
            //var fillBrush = shape.FillColor != null ? new SolidBrush(shape.FillColor.ToDrawingColor()) : null;

            //var point = GetOrigin(shape.Origin, shape.Position, shape.Size);

            //switch (shape.ShapeType)
            //{
            //    case ShapeType.Ellipse:
            //    if (fillBrush != null)
            //        _graphics.FillEllipse(fillBrush, point.X, point.Y, shape.Size.Width, shape.Size.Height);

            //    if (borderPen != null)
            //        _graphics.DrawEllipse(borderPen, point.X, point.Y, shape.Size.Width, shape.Size.Height);

            //    break;
            //    case ShapeType.Rectangle:
            //        if (fillBrush != null)
            //            _graphics.FillRectangle(fillBrush, point.X, point.Y, shape.Size.Width, shape.Size.Height);

            //        if (borderPen != null)
            //            _graphics.DrawRectangle(borderPen, point.X, point.Y, shape.Size.Width, shape.Size.Height);
            //        break;
            //    case ShapeType.LineList:
            //        if (borderPen != null)
            //            _graphics.DrawLines(borderPen, GetPointArray(shape.Points, point));

            //        break;
            //    case ShapeType.Polygon:
            //        if (fillBrush != null)
            //            _graphics.FillPolygon(fillBrush, GetPointArray(shape.Points, point));

            //        if (borderPen != null)
            //            _graphics.DrawLines(borderPen, GetPointArray(shape.Points, point));
            //        break;
            //}

            //if (fillBrush != null)
            //    fillBrush.Dispose();

            //if (borderPen != null)
            //    borderPen.Dispose();
        }

        public void Render(ISpriteLine line)
        {
            var pen = new Pen(line.LineColor.ToDrawingColor(), Convert.ToSingle(line.Width));
            //_graphics.DrawLine(pen, line.Start.X, line.Start.Y, line.End.X, line.End.Y);
            pen.Dispose();
        }

        public void Render()
        {
            var timer = System.Diagnostics.Stopwatch.StartNew();
            timer.Start();

            var fillTime = timer.ElapsedMilliseconds;

            timer.Reset();
            timer.Start();

            foreach (var drawable in _items)
                drawable.Render(this);

            var listTime = timer.ElapsedMilliseconds;
            timer.Reset();
            timer.Start();
            _display.Flip();

            var flipTime = timer.ElapsedMilliseconds;
            

            System.Diagnostics.Debug.WriteLine(string.Format("Fill {0} List {1} Render {2} ", fillTime, listTime, flipTime));
        }

        public void Resize(Size bounds)
        {
            _size = bounds;
            _scale = new Vector2(Convert.ToSingle(_size.Width) / Convert.ToSingle(_targetSize.Width), Convert.ToSingle(_size.Height) / Convert.ToSingle(_targetSize.Height));

            
        }

        public Size Size
        {
            get { return _size; }
        }

        public Size TargetSize
        {
            get { return _targetSize; }
        }

        public Vector2 ScaleFactor
        {
            get { return _scale; }
        }

        public void Invalidate()
        {
            _isSceneDirty = true;
        }

        #endregion

        public ISpriteImage CreateSpriteImage()
        {
            throw new NotImplementedException();
        }

        public ISpriteShape CreateRectangle()
        {
            throw new NotImplementedException();
        }

        public ISpriteText CreateText()
        {
            throw new NotImplementedException();
        }

        public ISpriteLine CreateLine()
        {
            throw new NotImplementedException();
        }

        public ISpriteShape CreateEllipse()
        {
            throw new NotImplementedException();
        }

        public ISpriteShape CreatePolygon()
        {
            throw new NotImplementedException();
        }

        public ISpriteShape CreateLineList()
        {
            throw new NotImplementedException();
        }


        #region IDisposable Members

        public void Dispose()
        {
            _display.Dispose();
            GapiDraw.Dispose();
        }

        #endregion
    }
}
